// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraPlayerController.h"
#include "CommonInputTypeEnum.h"
#include "Components/PrimitiveComponent.h"
#include "LyraLogChannels.h"
#include "LyraCheatManager.h"
#include "LyraPlayerState.h"
#include "Camera/LyraPlayerCameraManager.h"
#include "UI/LyraHUD.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "EngineUtils.h"
#include "LyraGameplayTags.h"
#include "GameFramework/Pawn.h"
#include "Net/UnrealNetwork.h"
#include "Engine/GameInstance.h"
#include "AbilitySystemGlobals.h"
#include "CommonInputSubsystem.h"
#include "LyraLocalPlayer.h"
#include "GameModes/LyraGameState.h"
#include "Settings/LyraSettingsLocal.h"
#include "Settings/LyraSettingsShared.h"
#include "Replays/LyraReplaySubsystem.h"
#include "ReplaySubsystem.h"
#include "Development/LyraDeveloperSettings.h"
#include "GameMapsSettings.h"
#if WITH_RPC_REGISTRY
#include "Tests/LyraGameplayRpcRegistrationComponent.h"
#include "HttpServerModule.h"
#endif

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraPlayerController) // 包含由UE生成的代码

namespace Lyra
{
	namespace Input
	{
		static int32 ShouldAlwaysPlayForceFeedback = 0; // 是否始终播放力反馈
		static FAutoConsoleVariableRef CVarShouldAlwaysPlayForceFeedback(TEXT("LyraPC.ShouldAlwaysPlayForceFeedback"),
			ShouldAlwaysPlayForceFeedback,
			TEXT("是否应该播放力反馈效果，即使最后一个输入设备不是游戏手柄？"),
			ECVF_Cheat);
	}
}

/**
 * 构造函数。
 */
ALyraPlayerController::ALyraPlayerController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	PlayerCameraManagerClass = ALyraPlayerCameraManager::StaticClass(); // 设置玩家摄像机管理器类

#if USING_CHEAT_MANAGER
	CheatClass = ULyraCheatManager::StaticClass(); // 设置作弊类
#endif // #if USING_CHEAT_MANAGER
}

/**
 * 预初始化组件。
 */
void ALyraPlayerController::PreInitializeComponents()
{
	Super::PreInitializeComponents(); // 调用父类方法
}

/**
 * 开始游戏时调用。
 */
void ALyraPlayerController::BeginPlay()
{
	Super::BeginPlay(); // 调用父类方法
	#if WITH_RPC_REGISTRY
	FHttpServerModule::Get().StartAllListeners(); // 启动所有HTTP监听器
	int32 RpcPort = 0;
	if (FParse::Value(FCommandLine::Get(), TEXT("rpcport="), RpcPort)) // 解析RPC端口
	{
		ULyraGameplayRpcRegistrationComponent* ObjectInstance = ULyraGameplayRpcRegistrationComponent::GetInstance(); // 获取RPC注册组件实例
		if (ObjectInstance && ObjectInstance->IsValidLowLevel()) // 如果实例有效
		{
			ObjectInstance->RegisterAlwaysOnHttpCallbacks(); // 注册始终启用的HTTP回调
			ObjectInstance->RegisterInMatchHttpCallbacks(); // 注册比赛中HTTP回调
		}
	}
	#endif
	SetActorHiddenInGame(false); // 设置角色在游戏中可见
}

/**
 * 结束游戏时调用。
 */
void ALyraPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason); // 调用父类方法
}

/**
 * 获取生命周期复制属性。
 */
void ALyraPlayerController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps); // 调用父类方法

	// 禁用复制PC目标视图，因为它对回放或客户端观察效果不好。
	// 引擎的TargetViewRotation仅在APlayerController::TickActor中设置，如果服务器提前知道正在观察特定角色，并且它只对COND_OwnerOnly进行复制。
	// 在客户端保存的回放中，COND_OwnerOnly永远不会为true，并且目标角色在录制时并不总是已知。
	// 为了支持客户端保存的回放，此复制已移至ReplicatedViewRotation并在PlayerTick中更新。
	DISABLE_REPLICATED_PROPERTY(APlayerController, TargetViewRotation); // 禁用复制目标视图旋转
}

/**
 * 接收玩家时调用。
 */
void ALyraPlayerController::ReceivedPlayer()
{
	Super::ReceivedPlayer(); // 调用父类方法
}

/**
 * 玩家每帧调用。
 */
void ALyraPlayerController::PlayerTick(float DeltaTime)
{
	Super::PlayerTick(DeltaTime); // 调用父类方法

	// 如果正在自动运行，则添加一些玩家输入
	if (GetIsAutoRunning()) // 如果正在自动运行
	{
		if (APawn* CurrentPawn = GetPawn()) // 获取当前角色
		{
			const FRotator MovementRotation(0.0f, GetControlRotation().Yaw, 0.0f); // 移动旋转
			const FVector MovementDirection = MovementRotation.RotateVector(FVector::ForwardVector); // 移动方向
			CurrentPawn->AddMovementInput(MovementDirection, 1.0f);	// 添加移动输入
		}
	}

	ALyraPlayerState* LyraPlayerState = GetLyraPlayerState(); // 获取Lyra玩家状态

	if (PlayerCameraManager && LyraPlayerState) // 如果摄像机管理器和玩家状态存在
	{
		APawn* TargetPawn = PlayerCameraManager->GetViewTargetPawn(); // 获取视图目标角色

		if (TargetPawn) // 如果目标角色存在
		{
			// 在服务器上更新视图旋转以便复制
			if (HasAuthority() || TargetPawn->IsLocallyControlled()) // 如果有权限或角色本地控制
			{
				LyraPlayerState->SetReplicatedViewRotation(TargetPawn->GetViewRotation()); // 设置复制的视图旋转
			}

			// 如果角色不是本地控制，更新目标视图旋转
			if (!TargetPawn->IsLocallyControlled()) // 如果角色不是本地控制
			{
				LyraPlayerState = TargetPawn->GetPlayerState<ALyraPlayerState>(); // 获取角色玩家状态
				if (LyraPlayerState) // 如果玩家状态存在
				{
					// 从被观察角色的玩家状态获取，可能与PC的玩家状态不同
					TargetViewRotation = LyraPlayerState->GetReplicatedViewRotation(); // 设置目标视图旋转
				}
			}
		}
	}
}

/**
 * 获取Lyra玩家状态。
 */
ALyraPlayerState* ALyraPlayerController::GetLyraPlayerState() const
{
	return CastChecked<ALyraPlayerState>(PlayerState, ECastCheckedType::NullAllowed); // 返回Lyra玩家状态
}

/**
 * 获取Lyra技能系统组件。
 */
ULyraAbilitySystemComponent* ALyraPlayerController::GetLyraAbilitySystemComponent() const
{
	const ALyraPlayerState* LyraPS = GetLyraPlayerState(); // 获取Lyra玩家状态
	return (LyraPS ? LyraPS->GetLyraAbilitySystemComponent() : nullptr); // 返回技能系统组件
}

/**
 * 获取Lyra HUD。
 */
ALyraHUD* ALyraPlayerController::GetLyraHUD() const
{
	return CastChecked<ALyraHUD>(GetHUD(), ECastCheckedType::NullAllowed); // 返回Lyra HUD
}

/**
 * 尝试录制客户端回放。
 */
bool ALyraPlayerController::TryToRecordClientReplay()
{
	// 查看是否应该录制回放
	if (ShouldRecordClientReplay()) // 如果应该录制回放
	{
		if (ULyraReplaySubsystem* ReplaySubsystem = GetGameInstance()->GetSubsystem<ULyraReplaySubsystem>()) // 获取回放子系统
		{
			APlayerController* FirstLocalPlayerController = GetGameInstance()->GetFirstLocalPlayerController(); // 获取第一个本地玩家控制器
			if (FirstLocalPlayerController == this) // 如果是第一个玩家
			{
				// 如果是第一个玩家，为本地回放更新观察者玩家状态，然后录制
				if (ALyraGameState* GameState = Cast<ALyraGameState>(GetWorld()->GetGameState())) // 获取游戏状态
				{
					GameState->SetRecorderPlayerState(PlayerState); // 设置录制者玩家状态

					ReplaySubsystem->RecordClientReplay(this); // 录制客户端回放
					return true; // 返回成功
				}
			}
		}
	}
	return false; // 返回失败
}

/**
 * 是否应该录制客户端回放。
 */
bool ALyraPlayerController::ShouldRecordClientReplay()
{
	UWorld* World = GetWorld(); // 获取世界
	UGameInstance* GameInstance = GetGameInstance(); // 获取游戏实例
	if (GameInstance != nullptr &&
		World != nullptr &&
		!World->IsPlayingReplay() && // 不在播放回放
		!World->IsRecordingClientReplay() && // 不在录制客户端回放
		NM_DedicatedServer != GetNetMode() && // 不是专用服务器
		IsLocalPlayerController()) // 是本地玩家控制器
	{
		FString DefaultMap = UGameMapsSettings::GetGameDefaultMap(); // 获取默认地图
		FString CurrentMap = World->URL.Map; // 获取当前地图

#if WITH_EDITOR
		CurrentMap = UWorld::StripPIEPrefixFromPackageName(CurrentMap, World->StreamingLevelsPrefix); // 去除PIE前缀
#endif
		if (CurrentMap == DefaultMap) // 如果是默认地图
		{
			// 永远不要在默认前端地图上录制演示，这可以用更好的检查来替换是否在主菜单中
			return false; // 返回false
		}

		if (UReplaySubsystem* ReplaySubsystem = GameInstance->GetSubsystem<UReplaySubsystem>()) // 获取回放子系统
		{
			if (ReplaySubsystem->IsRecording() || ReplaySubsystem->IsPlaying()) // 如果正在录制或播放
			{
				// 一次只能一个
				return false; // 返回false
			}
		}

		// 如果可能，现在检查设置
		if (const ULyraLocalPlayer* LyraLocalPlayer = Cast<ULyraLocalPlayer>(GetLocalPlayer())) // 获取本地玩家
		{
			if (LyraLocalPlayer->GetLocalSettings()->ShouldAutoRecordReplays()) // 如果应该自动录制回放
			{
				return true; // 返回true
			}
		}
	}
	return false; // 返回false
}

/**
 * 玩家状态队伍改变回调。
 */
void ALyraPlayerController::OnPlayerStateChangedTeam(UObject* TeamAgent, int32 OldTeam, int32 NewTeam)
{
	ConditionalBroadcastTeamChanged(this, IntegerToGenericTeamId(OldTeam), IntegerToGenericTeamId(NewTeam)); // 条件广播队伍改变
}

/**
 * 玩家状态改变回调。
 */
void ALyraPlayerController::OnPlayerStateChanged()
{
	// 空实现，供派生类实现而不需要挂钩所有其他事件
}

/**
 * 广播玩家状态改变。
 */
void ALyraPlayerController::BroadcastOnPlayerStateChanged()
{
	OnPlayerStateChanged(); // 调用玩家状态改变回调

	// 从旧玩家状态解绑（如果有）
	FGenericTeamId OldTeamID = FGenericTeamId::NoTeam; // 旧队伍ID
	if (LastSeenPlayerState != nullptr) // 如果最后看到的玩家状态存在
	{
		if (ILyraTeamAgentInterface* PlayerStateTeamInterface = Cast<ILyraTeamAgentInterface>(LastSeenPlayerState)) // 获取队伍代理接口
		{
			OldTeamID = PlayerStateTeamInterface->GetGenericTeamId(); // 获取旧队伍ID
			PlayerStateTeamInterface->GetTeamChangedDelegateChecked().RemoveAll(this); // 移除所有绑定
		}
	}

	// 绑定到新玩家状态（如果有）
	FGenericTeamId NewTeamID = FGenericTeamId::NoTeam; // 新队伍ID
	if (PlayerState != nullptr) // 如果玩家状态存在
	{
		if (ILyraTeamAgentInterface* PlayerStateTeamInterface = Cast<ILyraTeamAgentInterface>(PlayerState)) // 获取队伍代理接口
		{
			NewTeamID = PlayerStateTeamInterface->GetGenericTeamId(); // 获取新队伍ID
			PlayerStateTeamInterface->GetTeamChangedDelegateChecked().AddDynamic(this, &ThisClass::OnPlayerStateChangedTeam); // 绑定队伍改变事件
		}
	}

	// 广播队伍改变（如果确实改变）
	ConditionalBroadcastTeamChanged(this, OldTeamID, NewTeamID); // 条件广播队伍改变

	LastSeenPlayerState = PlayerState; // 更新最后看到的玩家状态
}

/**
 * 初始化玩家状态。
 */
void ALyraPlayerController::InitPlayerState()
{
	Super::InitPlayerState(); // 调用父类方法
	BroadcastOnPlayerStateChanged(); // 广播玩家状态改变
}

/**
 * 清理玩家状态。
 */
void ALyraPlayerController::CleanupPlayerState()
{
	Super::CleanupPlayerState(); // 调用父类方法
	BroadcastOnPlayerStateChanged(); // 广播玩家状态改变
}

/**
 * 玩家状态复制回调。
 */
void ALyraPlayerController::OnRep_PlayerState()
{
	Super::OnRep_PlayerState(); // 调用父类方法
	BroadcastOnPlayerStateChanged(); // 广播玩家状态改变

	// 当我们作为客户端连接到远程服务器时，玩家控制器可能比PlayerState和AbilitySystemComponent复制得晚。
	// 然而，TryActivateAbilitiesOnSpawn依赖于玩家控制器被复制以检查生成时技能是否应该在本地执行。
	// 因此，一旦PlayerController存在并解析了PlayerState，再次尝试激活生成时技能。
	// 在其他网络模式下，PlayerController永远不会延迟复制，因此LyraASC自己的TryActivateAbilitiesOnSpawn调用将成功。
	// 这里的处理仅适用于PlayerState和ASC在PC之前复制并错误地认为技能不是为本地玩家的情况。
	if (GetWorld()->IsNetMode(NM_Client)) // 如果是客户端
	{
		if (ALyraPlayerState* LyraPS = GetPlayerState<ALyraPlayerState>()) // 获取Lyra玩家状态
		{
			if (ULyraAbilitySystemComponent* LyraASC = LyraPS->GetLyraAbilitySystemComponent()) // 获取Lyra技能系统组件
			{
				LyraASC->RefreshAbilityActorInfo(); // 刷新技能角色信息
				LyraASC->TryActivateAbilitiesOnSpawn(); // 尝试激活生成时技能
			}
		}
	}
}

/**
 * 设置玩家。
 */
void ALyraPlayerController::SetPlayer(UPlayer* InPlayer)
{
	Super::SetPlayer(InPlayer); // 调用父类方法

	if (const ULyraLocalPlayer* LyraLocalPlayer = Cast<ULyraLocalPlayer>(InPlayer)) // 获取Lyra本地玩家
	{
		ULyraSettingsShared* UserSettings = LyraLocalPlayer->GetSharedSettings(); // 获取共享设置
		UserSettings->OnSettingChanged.AddUObject(this, &ThisClass::OnSettingsChanged); // 绑定设置改变事件

		OnSettingsChanged(UserSettings); // 调用设置改变回调
	}
}

/**
 * 设置改变回调。
 */
void ALyraPlayerController::OnSettingsChanged(ULyraSettingsShared* InSettings)
{
	bForceFeedbackEnabled = InSettings->GetForceFeedbackEnabled(); // 更新力反馈启用状态
}

/**
 * 添加作弊功能。
 */
void ALyraPlayerController::AddCheats(bool bForce)
{
#if USING_CHEAT_MANAGER
	Super::AddCheats(true); // 强制添加作弊
#else //#if USING_CHEAT_MANAGER
	Super::AddCheats(bForce); // 根据参数添加作弊
#endif // #else //#if USING_CHEAT_MANAGER
}

/**
 * 服务器作弊实现。
 */
void ALyraPlayerController::ServerCheat_Implementation(const FString& Msg)
{
#if USING_CHEAT_MANAGER
	if (CheatManager) // 如果作弊管理器存在
	{
		UE_LOG(LogLyra, Warning, TEXT("ServerCheat: %s"), *Msg); // 输出警告日志
		ClientMessage(ConsoleCommand(Msg)); // 执行控制台命令并发送消息给客户端
	}
#endif // #if USING_CHEAT_MANAGER
}

/**
 * 服务器作弊验证。
 */
bool ALyraPlayerController::ServerCheat_Validate(const FString& Msg)
{
	return true; // 总是验证通过
}

/**
 * 服务器为所有玩家作弊实现。
 */
void ALyraPlayerController::ServerCheatAll_Implementation(const FString& Msg)
{
#if USING_CHEAT_MANAGER
	if (CheatManager) // 如果作弊管理器存在
	{
		UE_LOG(LogLyra, Warning, TEXT("ServerCheatAll: %s"), *Msg); // 输出警告日志
		for (TActorIterator<ALyraPlayerController> It(GetWorld()); It; ++It) // 遍历所有Lyra玩家控制器
		{
			ALyraPlayerController* LyraPC = (*It); // 获取Lyra玩家控制器
			if (LyraPC) // 如果控制器存在
			{
				LyraPC->ClientMessage(LyraPC->ConsoleCommand(Msg)); // 执行控制台命令并发送消息给客户端
			}
		}
	}
#endif // #if USING_CHEAT_MANAGER
}

/**
 * 服务器为所有玩家作弊验证。
 */
bool ALyraPlayerController::ServerCheatAll_Validate(const FString& Msg)
{
	return true; // 总是验证通过
}

/**
 * 预处理输入。
 */
void ALyraPlayerController::PreProcessInput(const float DeltaTime, const bool bGamePaused)
{
	Super::PreProcessInput(DeltaTime, bGamePaused); // 调用父类方法
}

/**
 * 后处理输入。
 */
void ALyraPlayerController::PostProcessInput(const float DeltaTime, const bool bGamePaused)
{
	if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) // 获取Lyra技能系统组件
	{
		LyraASC->ProcessAbilityInput(DeltaTime, bGamePaused); // 处理技能输入
	}

	Super::PostProcessInput(DeltaTime, bGamePaused); // 调用父类方法
}

/**
 * 摄像机穿透目标时调用。
 */
void ALyraPlayerController::OnCameraPenetratingTarget()
{
	bHideViewTargetPawnNextFrame = true; // 设置下一帧隐藏视图目标角色
}

/**
 * 控制角色时调用。
 */
void ALyraPlayerController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn); // 调用父类方法

#if WITH_SERVER_CODE && WITH_EDITOR
	if (GIsEditor && (InPawn != nullptr) && (GetPawn() == InPawn)) // 如果在编辑器中且角色有效
	{
		for (const FLyraCheatToRun& CheatRow : GetDefault<ULyraDeveloperSettings>()->CheatsToRun) // 遍历要运行的作弊
		{
			if (CheatRow.Phase == ECheatExecutionTime::OnPlayerPawnPossession) // 如果在玩家角色控制时执行
			{
				ConsoleCommand(CheatRow.Cheat, /*bWriteToLog=*/ true); // 执行控制台命令
			}
		}
	}
#endif

	SetIsAutoRunning(false); // 设置自动运行为false
}

/**
 * 设置是否自动运行。
 */
void ALyraPlayerController::SetIsAutoRunning(const bool bEnabled)
{
	const bool bIsAutoRunning = GetIsAutoRunning(); // 获取当前自动运行状态
	if (bEnabled != bIsAutoRunning) // 如果状态改变
	{
		if (!bEnabled) // 如果禁用
		{
			OnEndAutoRun(); // 调用结束自动运行
		}
		else // 如果启用
		{
			OnStartAutoRun(); // 调用开始自动运行
		}
	}
}

/**
 * 获取是否自动运行。
 */
bool ALyraPlayerController::GetIsAutoRunning() const
{
	bool bIsAutoRunning = false;
	if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) // 获取Lyra技能系统组件
	{
		bIsAutoRunning = LyraASC->GetTagCount(LyraGameplayTags::Status_AutoRunning) > 0; // 检查自动运行标签计数
	}
	return bIsAutoRunning; // 返回自动运行状态
}

/**
 * 开始自动运行。
 */
void ALyraPlayerController::OnStartAutoRun()
{
	if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) // 获取Lyra技能系统组件
	{
		LyraASC->SetLooseGameplayTagCount(LyraGameplayTags::Status_AutoRunning, 1); // 设置自动运行标签计数
		K2_OnStartAutoRun(); // 调用蓝图事件
	}	
}

/**
 * 结束自动运行。
 */
void ALyraPlayerController::OnEndAutoRun()
{
	if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) // 获取Lyra技能系统组件
	{
		LyraASC->SetLooseGameplayTagCount(LyraGameplayTags::Status_AutoRunning, 0); // 设置自动运行标签计数为0
		K2_OnEndAutoRun(); // 调用蓝图事件
	}
}

/**
 * 更新力反馈。
 */
void ALyraPlayerController::UpdateForceFeedback(IInputInterface* InputInterface, const int32 ControllerId)
{
	if (bForceFeedbackEnabled) // 如果力反馈启用
	{
		if (const UCommonInputSubsystem* CommonInputSubsystem = UCommonInputSubsystem::Get(GetLocalPlayer())) // 获取通用输入子系统
		{
			const ECommonInputType CurrentInputType = CommonInputSubsystem->GetCurrentInputType(); // 获取当前输入类型
			if (Lyra::Input::ShouldAlwaysPlayForceFeedback || CurrentInputType == ECommonInputType::Gamepad || CurrentInputType == ECommonInputType::Touch) // 如果应该始终播放力反馈或输入类型是游戏手柄或触摸
			{
				InputInterface->SetForceFeedbackChannelValues(ControllerId, ForceFeedbackValues); // 设置力反馈通道值
				return;
			}
		}
	}
	
	InputInterface->SetForceFeedbackChannelValues(ControllerId, FForceFeedbackValues()); // 设置力反馈通道值为空
}

/**
 * 更新隐藏组件。
 */
void ALyraPlayerController::UpdateHiddenComponents(const FVector& ViewLocation, TSet<FPrimitiveComponentId>& OutHiddenComponents)
{
	Super::UpdateHiddenComponents(ViewLocation, OutHiddenComponents); // 调用父类方法

	if (bHideViewTargetPawnNextFrame) // 如果下一帧需要隐藏视图目标角色
	{
		AActor* const ViewTargetPawn = PlayerCameraManager ? Cast<AActor>(PlayerCameraManager->GetViewTarget()) : nullptr; // 获取视图目标角色
		if (ViewTargetPawn) // 如果视图目标角色存在
		{
			// 内部辅助函数，隐藏所有组件
			auto AddToHiddenComponents = [&OutHiddenComponents](const TInlineComponentArray<UPrimitiveComponent*>& InComponents)
			{
				// 添加每个组件和所有附加的子组件
				for (UPrimitiveComponent* Comp : InComponents)
				{
					if (Comp->IsRegistered()) // 如果组件已注册
					{
						OutHiddenComponents.Add(Comp->GetPrimitiveSceneId()); // 添加到隐藏组件

						for (USceneComponent* AttachedChild : Comp->GetAttachChildren()) // 遍历附加子组件
						{
							static FName NAME_NoParentAutoHide(TEXT("NoParentAutoHide")); // 无父级自动隐藏标签
							UPrimitiveComponent* AttachChildPC = Cast<UPrimitiveComponent>(AttachedChild); // 转换为原始组件
							if (AttachChildPC && AttachChildPC->IsRegistered() && !AttachChildPC->ComponentTags.Contains(NAME_NoParentAutoHide)) // 如果子组件有效且不包含无父级自动隐藏标签
							{
								OutHiddenComponents.Add(AttachChildPC->GetPrimitiveSceneId()); // 添加到隐藏组件
							}
						}
					}
				}
			};

			//TODO 使用接口解决。收集隐藏组件或类似的东西。
			//TODO 隐藏并不完美，有时你想要淡出效果而不是接近时隐藏，需要向设计师反馈。

			// 隐藏角色的组件
			TInlineComponentArray<UPrimitiveComponent*> PawnComponents; // 角色组件数组
			ViewTargetPawn->GetComponents(PawnComponents); // 获取角色组件
			AddToHiddenComponents(PawnComponents); // 添加到隐藏组件

			//// 也隐藏武器
			//if (ViewTargetPawn->CurrentWeapon) // 如果当前武器存在
			//{
			//	TInlineComponentArray<UPrimitiveComponent*> WeaponComponents; // 武器组件数组
			//	ViewTargetPawn->CurrentWeapon->GetComponents(WeaponComponents); // 获取武器组件
			//	AddToHiddenComponents(WeaponComponents); // 添加到隐藏组件
			//}
		}

		// 我们已消费它，为下一帧重置
		bHideViewTargetPawnNextFrame = false; // 重置隐藏标志
	}
}

/**
 * 设置通用队伍ID。
 */
void ALyraPlayerController::SetGenericTeamId(const FGenericTeamId& NewTeamID)
{
	UE_LOG(LogLyraTeams, Error, TEXT("你不能在玩家控制器(%s)上设置队伍ID；它由关联的玩家状态驱动"), *GetPathNameSafe(this)); // 输出错误日志
}

/**
 * 获取通用队伍ID。
 */
FGenericTeamId ALyraPlayerController::GetGenericTeamId() const
{
	if (const ILyraTeamAgentInterface* PSWithTeamInterface = Cast<ILyraTeamAgentInterface>(PlayerState)) // 获取队伍代理接口
	{
		return PSWithTeamInterface->GetGenericTeamId(); // 返回队伍ID
	}
	return FGenericTeamId::NoTeam; // 返回无队伍
}

/**
 * 获取队伍索引改变委托。
 */
FOnLyraTeamIndexChangedDelegate* ALyraPlayerController::GetOnTeamIndexChangedDelegate()
{
	return &OnTeamChangedDelegate; // 返回队伍改变委托
}

/**
 * 取消控制时调用。
 */
void ALyraPlayerController::OnUnPossess()
{
	// 确保被取消控制的角色不会保留为我们ASC的头像角色
	if (APawn* PawnBeingUnpossessed = GetPawn()) // 获取被取消控制的角色
	{
		if (UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(PlayerState)) // 获取技能系统组件
		{
			if (ASC->GetAvatarActor() == PawnBeingUnpossessed) // 如果头像角色是被取消控制的角色
			{
				ASC->SetAvatarActor(nullptr); // 设置头像角色为空
			}
		}
	}

	Super::OnUnPossess(); // 调用父类方法
}

//////////////////////////////////////////////////////////////////////
// ALyraReplayPlayerController

/**
 * 回放玩家控制器每帧调用。
 */
void ALyraReplayPlayerController::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds); // 调用父类方法

	// 状态可能随时因回放期间的擦洗而无效
	if (!IsValid(FollowedPlayerState)) // 如果被跟随的玩家状态无效
	{
		UWorld* World = GetWorld(); // 获取世界

		// 监听录制和播放的改变
		if (ALyraGameState* GameState = Cast<ALyraGameState>(World->GetGameState())) // 获取游戏状态
		{
			if (!GameState->OnRecorderPlayerStateChangedEvent.IsBoundToObject(this)) // 如果未绑定事件
			{
				GameState->OnRecorderPlayerStateChangedEvent.AddUObject(this, &ThisClass::RecorderPlayerStateUpdated); // 绑定录制者玩家状态改变事件
			}
			if (APlayerState* RecorderState = GameState->GetRecorderPlayerState()) // 获取录制者玩家状态
			{
				RecorderPlayerStateUpdated(RecorderState); // 调用录制者玩家状态更新回调
			}
		}
	}
}

/**
 * 平滑目标视图旋转。
 */
void ALyraReplayPlayerController::SmoothTargetViewRotation(APawn* TargetPawn, float DeltaSeconds)
{
	// 默认行为是插值到TargetViewRotation，该值从APlayerController::TickActor设置，但不太平滑

	Super::SmoothTargetViewRotation(TargetPawn, DeltaSeconds); // 调用父类方法
}

/**
 * 是否应该录制客户端回放。
 */
bool ALyraReplayPlayerController::ShouldRecordClientReplay()
{
	return false; // 回放玩家控制器不录制回放
}

/**
 * 录制者玩家状态更新回调。
 */
void ALyraReplayPlayerController::RecorderPlayerStateUpdated(APlayerState* NewRecorderPlayerState)
{
	if (NewRecorderPlayerState) // 如果新的录制者玩家状态存在
	{
		FollowedPlayerState = NewRecorderPlayerState; // 更新被跟随的玩家状态

		// 绑定到角色改变事件并立即调用
		NewRecorderPlayerState->OnPawnSet.AddUniqueDynamic(this, &ALyraReplayPlayerController::OnPlayerStatePawnSet); // 绑定玩家状态角色设置事件
		OnPlayerStatePawnSet(NewRecorderPlayerState, NewRecorderPlayerState->GetPawn(), nullptr); // 调用玩家状态角色设置回调
	}
}

/**
 * 玩家状态角色设置回调。
 */
void ALyraReplayPlayerController::OnPlayerStatePawnSet(APlayerState* ChangedPlayerState, APawn* NewPlayerPawn, APawn* OldPlayerPawn)
{
	if (ChangedPlayerState == FollowedPlayerState) // 如果改变的玩家状态是被跟随的玩家状态
	{
		SetViewTarget(NewPlayerPawn); // 设置视图目标
	}
}